package org.nskmda.gc.servlets;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheManager;

import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;

/**
 * Servlet implementation class TestServletReload
 */
public class LoadDataServlet extends HttpServlet {
	static final long serialVersionUID = 1L;
	static final Logger LOGGER = Logger.getLogger(LoadDataServlet.class.getName()); 
	
	final URLFetchService urlFetchSvc = URLFetchServiceFactory.getURLFetchService();
	Cache cacheData;
	
	final Set<String> comicsNames = new TreeSet<String>(Arrays.asList("duplex", "poochcafe", "pearlsbeforeswine"));
	
	static final String COMICS_NAME_PARAM = "comics";
	static final String COMICS_DATE_PARAM = "date";
	static final String COMICS_WIDTH_PARAM = "size";

	final SimpleDateFormat comicsDateFormat = new SimpleDateFormat("yyyy/MM/dd");

	static final String BASE_HTML_URL_FORMAT = "http://www.gocomics.com/%s/%s";
	static final String KEY_FORMAT = "%s~%s";
	static final String TARGET_URL_FORMAT = "%s?width=%s";
	
    /**
     * @see HttpServlet#HttpServlet()
     */
    public LoadDataServlet() {
        super();
    }

    @Override
    public void init() throws ServletException {
    	Map<Object, Object> cacheInitProperties = new HashMap<>();
    	cacheInitProperties.put(MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT, true);
    	
    	try {
			this.cacheData = CacheManager.getInstance().getCacheFactory().createCache(cacheInitProperties);
		} catch (CacheException e) {
			LOGGER.log(Level.WARNING, "Cache initialization failed", e);
		}
    }
    
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/plain");
		String comicsName = request.getParameter(COMICS_NAME_PARAM);
		String comicsDate = URLDecoder.decode(request.getParameter(COMICS_DATE_PARAM), "UTF-8");
		String imageWidth = URLDecoder.decode(request.getParameter(COMICS_WIDTH_PARAM), "UTF-8");
		
		// Validate request
		if (!this.validateParameters(comicsName, comicsDate, imageWidth)) {
			this.sendNotFound(response);
			return;
		}
		
		String cacheKey = String.format(KEY_FORMAT, comicsName, comicsDate);
		String imageUrl = this.checkCacheForUrl(cacheKey);
		if (imageUrl!=null) {
			response.getWriter().append(imageUrl);
			return;
		}
		
		imageUrl = this.loadUrlFromHTML(comicsName, comicsDate);
		if (imageUrl==null) {
			this.sendNotFound(response);
			return;
		}
		
		imageUrl = String.format(TARGET_URL_FORMAT, imageUrl, imageWidth);
		this.saveCacheValue(cacheKey, imageUrl);
		response.getWriter().append(imageUrl);
		
	}
	
	void sendNotFound(HttpServletResponse response) throws IOException {
		response.sendError(HttpServletResponse.SC_NOT_FOUND);
	}
	
	boolean validateParameters(String name, String date, String width) {
		if (name==null || !comicsNames.contains(name)) {
			LOGGER.log(Level.WARNING, "Comics name ["+name+"] is invalid");
			return false;
		}

		if (date == null) {
			LOGGER.log(Level.WARNING, "Date param ["+date+"] is invalid");
			return false;
		}
		
		try {
			comicsDateFormat.parse(date);
		} catch (ParseException e) {
			LOGGER.log(Level.WARNING, "Date param ["+date+"] is incorrectly formatted");
			return false;
		}
		if (width == null || "".equals(width.trim())) {
			LOGGER.log(Level.WARNING, "Comics width ["+width+"] is invalid");
			return false;
		}
		try {
			if (!width.equals(Integer.valueOf(width.trim()).toString())) {
				LOGGER.log(Level.WARNING, "Comics width ["+width+"] is invalid");
				return false;
			}
		} catch (NumberFormatException nfe) {
			LOGGER.log(Level.WARNING, "Comics width ["+width+"] is incorrectly formatted");
			return false;
		}
		
		return true;
	}
	
	String loadUrlFromHTML(String comicsName, String comicsDate) throws MalformedURLException, IOException {
		String targetUrl = String.format(BASE_HTML_URL_FORMAT, comicsName, comicsDate);
		
		HTTPResponse resp = this.urlFetchSvc.fetch(new URL(targetUrl));
		LOGGER.log(Level.FINER, "Fetching page {0}", targetUrl);
		if (HttpServletResponse.SC_OK != resp.getResponseCode()) {
			LOGGER.log(Level.WARNING, "Page {} load failed. Exiting", targetUrl);
			return null;
		}
		LOGGER.log(Level.FINER, "Fetched {0} kB", new Object[]{resp.getContent().length/1024});
		
		String respStr = new String(resp.getContent());
		String tmpl1 = "<p class=\"feature_item\" data-id=\"";
		int pos1 = respStr.indexOf(tmpl1);
		if (pos1<0)
		{
			LOGGER.log(Level.WARNING, "Data ID element not found. Exiting");
			return null;
		}
		LOGGER.log(Level.FINER, String.format("P position is [%s]", pos1));
		
		int pos2 = respStr.indexOf("\"", pos1+tmpl1.length());
		if (pos2<0) {
			LOGGER.log(Level.WARNING, "Data ID element closing quote not found. Exiting");
			return null;
		}
		
		String dataId = respStr.substring(pos1+tmpl1.length(), pos2);
		
		LOGGER.log(Level.FINER, String.format("Data id closing position is [%s]", pos2));
		LOGGER.log(Level.FINER, String.format("Data id=[%s]", dataId));
		
		String tmpl2 = "<div id='mutable_%s' style='display: none;'>";
		String tmpl2Value = String.format(tmpl2, dataId);

		LOGGER.log(Level.FINER, String.format("Looking up Image DIV [%s] starting at [%s]", tmpl2Value, pos2));
		
		int pos3 = respStr.indexOf(tmpl2Value, pos2);
		if (pos3<0) {
			LOGGER.log(Level.WARNING, "Image DIV for data ID=[{}] not found. Exiting", dataId);
			return null;
		}
		
		LOGGER.log(Level.FINER, "Image URL DIV found");
		
		String tmpl3 = "class=\"strip\" src=\"";
		int pos4 = respStr.indexOf(tmpl3, pos3+tmpl2.length()); 
		if (pos4<0) {
			LOGGER.log(Level.WARNING, "Full image URL not found. Exiting");
			return null;
		}
		
		// Potential fall back - look for closing quote
		int pos5 = respStr.indexOf("\"", pos4 + tmpl3.length());
		if (pos5<0) {
			LOGGER.log(Level.WARNING, "Image URL end is not found. Exiting");
			return null;
		}
		String targetImage = respStr.substring(pos4+tmpl3.length(), pos5);
		LOGGER.log(Level.FINE, String.format("URL found [%s]", targetImage));
		return targetImage;
	}

	String loadUrlFromXML() {
		return "";
	}
	
	String checkCacheForUrl(String key) {
		if (this.cacheData == null) {
			return null;
		}
		Object value = this.cacheData.get(key);
		return value==null ? null : value.toString();
	}
	
	void saveCacheValue(String key, String value) {
		if (this.cacheData!=null) {
			this.cacheData.put(key, value);
		}
	}
	
	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request, response);
	}
}